﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using System.Text.RegularExpressions;

namespace SvnNotes
{
    /// <summary>
    /// Parses out notes from an SVN xml log
    /// </summary>
    public class SvnNotesParser
    {
        public const string DefaultSvnNotesRegexString = @"(?<tag>[A-Z]+)([\s]+)?(?<id>\[#[0-9]+\])?([\s]+)?:(?<note>.*)$";
        private Regex _SvnNotesRegex;
        private Stream _Stream = null;
        public SvnNotesParser(Stream svnXmlStream)
            : this()
        {
            if (svnXmlStream == null) throw new ArgumentNullException("svnXmlStream");

            _Stream = svnXmlStream;
        }
        public SvnNotesParser()
        {
            _SvnNotesRegex = new Regex(String.IsNullOrEmpty(Config.SvnNotesRegEx) ? DefaultSvnNotesRegexString : Config.SvnNotesRegEx, RegexOptions.Multiline);
            string[] groupNames = _SvnNotesRegex.GetGroupNames();
            if (!groupNames.Contains("id")) throw new InvalidProgramException("SvnNotesRegEx requires a group named id");
            if (!groupNames.Contains("note")) throw new InvalidProgramException("SvnNotesRegEx requires a group named note");
            if (!groupNames.Contains("tag")) throw new InvalidProgramException("SvnNotesRegEx requires a group named tag");
        }
        private Dictionary<string, EntryCollection> _Entries = new Dictionary<string, EntryCollection>();

        /// <summary>
        /// Parse the given svn xml into a dictionary of entries by tag
        /// </summary>
        /// <returns></returns>
        public Dictionary<string, EntryCollection> Parse()
        {
            _Entries.Clear();

            XmlTextReader reader = new XmlTextReader(_Stream);
            while (reader.Read())
            {
                if (reader.NodeType == XmlNodeType.Element && reader.Name == "logentry")
                {
                    string author = null;
                    DateTime date = DateTime.MinValue;
                    string message = null;
                    long revision = 0;

                    reader.MoveToAttribute("revision");
                    revision = Convert.ToInt64(reader.Value);

                    while (reader.Read())
                    {
                        if (reader.NodeType == XmlNodeType.EndElement && reader.Name == "logentry")
                        {
                            break;
                        }
                        if (reader.NodeType == XmlNodeType.Element)
                        {
                            if (reader.Name.ToLower() == "author")
                            {
                                author = reader.ReadString();
                            }
                            if (reader.Name.ToLower() == "date")
                            {
                                date = reader.ReadElementContentAsDateTime();

                            }
                            if (reader.Name.ToLower() == "msg")
                            {
                                message = reader.ReadString();
                            }
                        }
                    }

                    if (!String.IsNullOrEmpty(message))
                    {
                        foreach (Match match in _SvnNotesRegex.Matches(message))
                        {
                            EntryCollection list = GetEntryCollectionByLogTag(match.Groups["tag"].Value);
                            list.Add(new Entry
                            {
                                Id = match.Groups["id"].Value.Trim(),
                                Revision = revision,
                                Date = date,
                                Note = match.Groups["note"].Value.Trim()
                            });
                        }

                    }
                }
            }


            return _Entries;
           
        }

        private EntryCollection GetEntryCollectionByLogTag(string logTag)
        {
            if (!_Entries.Keys.Contains(logTag))
            {
                EntryCollection list = new EntryCollection();
                _Entries.Add(logTag, list);
                return list;
            }
            else
            {
                return _Entries[logTag];
            }
        }
    }
}
